//
//  RangeSlider.swift
//  SwiftyRangeSlider
//
//  Created by zhuxuhong on 2022/2/23.
//

import UIKit

public class RangeSlider: UIControl {
    @IBInspectable public var minValue: CGFloat = 0{
        didSet{
            updateLayouts()
        }
    }
    @IBInspectable public var maxValue: CGFloat = 1{
        didSet{
            updateLayouts()
        }
    }
    @IBInspectable public var lowerValue: CGFloat = 0.2 {
        didSet{
            updateLayouts()
        }
    }
    @IBInspectable public var upperValue: CGFloat = 0.8{
        didSet{
            updateLayouts()
        }
    }
    @IBInspectable public var trackHeight: CGFloat = 4 {
        didSet{
            trackLayer.cornerRadius = trackHeight/2
            updateLayouts()
        }
    }
    @IBInspectable public var trackColor: UIColor = UIColor(white: 0.9, alpha: 1) {
        didSet{
            trackLayer.setNeedsDisplay()
        }
    }
    @IBInspectable public var trackTintColor: UIColor = UIView().tintColor {
        didSet{
            trackLayer.setNeedsDisplay()
        }
    }
    
    @IBInspectable public var thumbImage = UIImage.defaultThumbImage {
        didSet{
            lowerThumbIV.image = thumbImage
            upperThumbIV.image = thumbImage
            updateLayouts()
        }
    }
    @IBInspectable public var thumbHighlightedImage = UIImage.defaultThumbHighlightImage {
        didSet{
            lowerThumbIV.highlightedImage = thumbHighlightedImage
            upperThumbIV.highlightedImage = thumbHighlightedImage
            updateLayouts()
        }
    }
    
    private var previousLocation = CGPoint.zero
    
    private lazy var trackLayer: RangeSliderTrackLayer = {
        let layer = RangeSliderTrackLayer()
        layer.slider = self
        layer.contentsScale = UIScreen.main.scale
        return layer
    }()
    private let lowerThumbIV = UIImageView()
    private let upperThumbIV = UIImageView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addViews()
    }
    
    private func addViews() {
        layer.addSublayer(trackLayer)
        
        lowerThumbIV.clipsToBounds = false
        upperThumbIV.clipsToBounds = false
        lowerThumbIV.image = thumbImage
        lowerThumbIV.highlightedImage = thumbHighlightedImage
        addSubview(lowerThumbIV)
        
        upperThumbIV.image = thumbImage
        upperThumbIV.highlightedImage = thumbHighlightedImage
        addSubview(upperThumbIV)
    }
    
    public override func layoutSubviews() {
        super.layoutSubviews()
        
        updateLayouts()
    }
    
    private func updateLayouts() {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        
        trackLayer.frame = bounds.insetBy(dx: 0.0, dy: (bounds.height-trackHeight) / 2)
        trackLayer.setNeedsDisplay()
        lowerThumbIV.frame = CGRect(origin: thumbOriginForValue(lowerValue),
                                    size: thumbImage.size)
        upperThumbIV.frame = CGRect(origin: thumbOriginForValue(upperValue),
                                    size: thumbImage.size)
        
        CATransaction.commit()
    }
    
    private var maxTrackWidth: CGFloat {
        return bounds.width - thumbImage.size.width
    }
    
    func positionForValue(_ value: CGFloat) -> CGFloat {
        return min(maxTrackWidth, max(0, maxTrackWidth * value))
    }
    private func thumbOriginForValue(_ value: CGFloat) -> CGPoint {
        let x = positionForValue(value)
        return CGPoint(x: x, y: (bounds.height - thumbImage.size.height) / 2.0)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        
        addViews()
    }
}

extension RangeSlider {
    public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        previousLocation = touch.location(in: self)
        
        let lowerIn = lowerThumbIV.frame.contains(previousLocation)
        let upperIn = upperThumbIV.frame.contains(previousLocation)
        
        switch (lowerIn, upperIn) {
        case (true, false):
            lowerThumbIV.isHighlighted = true
        case (false, true):
            upperThumbIV.isHighlighted = true
        case (true, true):
            lowerThumbIV.isHighlighted = true
            upperThumbIV.isHighlighted = true
        default:
            break
        }
        
        return lowerThumbIV.isHighlighted || upperThumbIV.isHighlighted
    }
    
    public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        let loc = touch.location(in: self)
        
        let scale = (maxValue - minValue) / maxTrackWidth
        let deltaX = loc.x - previousLocation.x
        let deltaValue = deltaX * scale
        
        previousLocation = loc
        
        func updateLowerValue() {
            lowerValue += deltaValue
            lowerValue = boundValue(lowerValue, toLowerValue: minValue, upperValue: upperValue)
        }
        func updateUpperValue() {
            upperValue += deltaValue
            upperValue = boundValue(upperValue, toLowerValue: lowerValue, upperValue: maxValue)
        }
        
        switch (lowerThumbIV.isHighlighted, upperThumbIV.isHighlighted, deltaX <= 0) {
        case (true, false, _):
            updateLowerValue()
        case (true, true, true):
            updateLowerValue()
            upperThumbIV.isHighlighted = false
        case (false, true, _):
            updateUpperValue()
        case (true, true, false):
            updateUpperValue()
            lowerThumbIV.isHighlighted = false
        default: break
        }
        
        updateLayouts()
        
        sendActions(for: .valueChanged)
        
        return true
    }
    
    private func boundValue(
        _ value: CGFloat,
        toLowerValue lowerValue: CGFloat,
        upperValue: CGFloat
    ) -> CGFloat {
        return min(max(value, lowerValue), upperValue)
    }
    
    public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
        lowerThumbIV.isHighlighted = false
        upperThumbIV.isHighlighted = false
    }
}

extension UIImage {
    static var defaultThumbImage: UIImage = {
        let rect = CGRect(origin: .zero, size: .init(width: 30, height: 30))
        UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
        let ctx = UIGraphicsGetCurrentContext()
        ctx?.setFillColor(UIColor.white.cgColor)
        ctx?.setStrokeColor(UIColor(white: 0.9, alpha: 1).cgColor)
        ctx?.fillEllipse(in: rect)
        ctx?.strokeEllipse(in: rect.insetBy(dx: 0.5, dy: 0.5))
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img ?? .init()
    }()
    static var defaultThumbHighlightImage: UIImage = {
        let rect = CGRect(origin: .zero, size: .init(width: 30, height: 30))
        UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
        let ctx = UIGraphicsGetCurrentContext()
        ctx?.setFillColor(UIColor(white: 0.95, alpha: 1).cgColor)
        ctx?.setStrokeColor(UIColor(white: 0.9, alpha: 1).cgColor)
        ctx?.fillEllipse(in: rect)
        ctx?.strokeEllipse(in: rect.insetBy(dx: 0.5, dy: 0.5))
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img ?? .init()
    }()
}
